def _spawn(self): """ function to create the grid by spawning fishes and sharks initially (and only at start) :return: """ # get simulation elements simulation_params = self.get_simulation_parameters(self._sid) grid_size = simulation_params.grid_size coord_array = [(x, y) for x in range(grid_size) for y in range(grid_size)] random.shuffle(coord_array) # spawn fish and Sharks fishes = 0 sharks = 0 for coord in coord_array: if fishes < simulation_params.init_nb_fish: self._persistence.init_animal( sim_id=self._sid, current_turn=0, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(*coord)) fishes += 1 elif sharks < simulation_params.init_nb_shark: self._persistence.init_animal( sim_id=self._sid, current_turn=0, animal_type=Animal.Shark, coordinate=SquareGridCoordinate(*coord)) sharks += 1 else: break return
def _spawn(self): """ function to create the grid by spawning fishes and sharks initially (and only at start) :return: """ # get simulation elements simulation_params = self.simulation_params grid_size = simulation_params.grid_size coord_array = [(x, y) for x in range(grid_size) for y in range(grid_size)] random.shuffle(coord_array) # spawn fish and Sharks fishes = 0 sharks = 0 for coord in coord_array: if fishes < simulation_params.init_nb_fish: # since animal at start can be able to breed, last breed can be negative spawn_turn = -random.randint(0, simulation_params.fish_breed_maturity) self._persistence.init_animal(sim_id=self._sid, current_turn=spawn_turn, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(*coord), last_breed=spawn_turn) fishes += 1 elif sharks < simulation_params.init_nb_shark: spawn_turn = -random.randint(0, simulation_params.shark_breed_maturity) self._persistence.init_animal(sim_id=self._sid, current_turn=spawn_turn, animal_type=Animal.Shark, coordinate=SquareGridCoordinate(*coord), last_breed=spawn_turn) sharks += 1 else: break return
def test_neighbour_function(self): # full neighborhood neigh_list = square_grid_neighbours(10, SquareGridCoordinate(1, 1)) assert len(neigh_list) == 8 # corner neigh_list = square_grid_neighbours(10, SquareGridCoordinate(9, 9)) assert len(neigh_list) == 3 # line neigh_list = square_grid_neighbours(10, SquareGridCoordinate(0, 5)) assert len(neigh_list) == 5
def test_valid_coordinate(self): assert square_grid_valid(10, SquareGridCoordinate(1, 2)) # raise if invalid with pytest.raises(TopologyError): square_grid_valid(10, SquareGridCoordinate(10, 2)) # invalid not raised assert not square_grid_valid( 10, SquareGridCoordinate(-1, 2), raise_err=False) assert not square_grid_valid( 10, SquareGridCoordinate(1, -1), raise_err=False) assert not square_grid_valid( 10, SquareGridCoordinate(1, 10), raise_err=False)
def test_square_grid_coordinate(self): coord = SquareGridCoordinate(1, 2) assert coord.x == 1 assert coord.y == 2 moved = coord.move(1, 1) assert moved.x == 2 assert moved.y == 3 coord_2 = SquareGridCoordinate(1, 3) coord_3 = SquareGridCoordinate(1, 2) assert not coord == coord_2 assert coord == coord_3
def test_square_grid_coordinate(self): coord = SquareGridCoordinate(1, 2) assert coord.x == 1 assert coord.y == 2 moved = coord.move(1, 1, grid_size=10) # AM: add grid_size as new argument assert moved.x == 2 assert moved.y == 3 coord_2 = SquareGridCoordinate(1, 3) coord_3 = SquareGridCoordinate(1, 2) assert not coord == coord_2 assert coord == coord_3
def test_neighbour_function(self): # full neighborhood neigh_list = square_grid_neighbours(10, SquareGridCoordinate(1, 1)) assert len(neigh_list) == 8 # corner neigh_list = square_grid_neighbours(10, SquareGridCoordinate(9, 9)) assert len( neigh_list ) == 8 # Used to work fine. Changed from 3, since now we have 'infinite' grid # line neigh_list = square_grid_neighbours(10, SquareGridCoordinate(0, 5)) assert len( neigh_list ) == 8 # Used to work fine. Changed from 5, since now we have 'infinite' grid
def test_topology_agnostic_eating(self): ''' Check the topology-agnostic behavior, i.e. that the shark from one corner will eat the fish from the opposite corner ''' # set fish breeding probability to 0 sim_config_empty_local = copy.deepcopy(sim_config_empty) sim_config_empty_local['fish_breed_probability'] = 0 client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty_local) a_list = [(Animal.Fish, SquareGridCoordinate(x=0, y=0)), (Animal.Shark, SquareGridCoordinate(x=9, y=9))] # adding some food and a shark for t, c in a_list: client.init_animal(sim_id=grid._sid, current_turn=0, animal_type=t, coordinate=c) # updating grid turn grid._sim_turn = 6 # AM: the latest two rows to allow optimized 'occupied coordinates' check grid.animals = grid.get_simulation_grid_data() grid.occupied_coord = set( zip(grid.animals.coord_x, grid.animals.coord_y)) shark_update = grid._eat() # shark has eaten shark = grid._persistence.get_animals_by_type( sim_id=grid._sid, animal_type=Animal.Shark).iloc[0] # the shark is in update list assert len( shark_update) == 1, 'There should be one shark in update list' assert shark.last_fed == 6, 'Shark last fed value should have updated' assert shark_update[shark.oid] == SquareGridCoordinate( x=9, y=9), 'Shark previous coordinate in shark update' # Fish is dead animal_in_square = client.get_animal_in_position( sim_id=grid._sid, coordinate=SquareGridCoordinate(int(shark.coord_x), int(shark.coord_y)), live_only=False) assert len( animal_in_square) == 2, 'there shoud be two animals in that square' # check that there is no fish fish = grid._persistence.get_animals_by_type(sim_id=grid._sid, animal_type=Animal.Fish) assert len( fish ) == 0, 'there should be no fish alive' # there is no fish (since we put 0 breeding probability)
def test_position_functions(self): client = SimulationClient('sqlite:///:memory:') # init DB sid = client.init_simulation(**sim_config) # add a bunch of animals for t, c in animal_list: client.init_animal(sim_id=sid, current_turn=0, animal_type=t, coordinate=c) # check animal in square coord = SquareGridCoordinate(x=1, y=3) animals_in_square = client.get_animal_in_position(sim_id=sid, coordinate=coord) assert len(animals_in_square) == 1 # kill that animal client.kill_animal(sim_id=sid, animal_ids=[animals_in_square[0].oid]) animals_in_square = client.get_animal_in_position(sim_id=sid, coordinate=coord) assert len(animals_in_square) == 0 # adding a live one in coord client.init_animal(sim_id=sid, current_turn=0, animal_type=Animal.Fish, coordinate=coord) animals_in_square = client.get_animal_in_position(sim_id=sid, coordinate=coord, live_only=False) assert len(animals_in_square) == 2
def test_check_if_occupied(self): ''' Test the modified function ''' client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty) a_list = [(Animal.Fish, SquareGridCoordinate(x=1, y=1)), (Animal.Shark, SquareGridCoordinate(x=2, y=2))] # adding some food and a shark for t, c in a_list: client.init_animal(sim_id=grid._sid, current_turn=0, animal_type=t, coordinate=c) grid.animals = grid.get_simulation_grid_data() grid.occupied_coord = set( zip(grid.animals.coord_x, grid.animals.coord_y)) assert grid.check_if_occupied(SquareGridCoordinate( x=1, y=1)) == True, 'should be occupied' assert grid.check_if_occupied(SquareGridCoordinate( x=2, y=2)) == True, 'should be occupied' assert grid.check_if_occupied(SquareGridCoordinate( x=1, y=2)) == False, 'should be spare' # repeat the same for 1x1 case with initialization of 1 Fish del client, grid, a_list sim_config_empty_local = copy.deepcopy(sim_config_empty) sim_config_empty_local['init_nb_fish'] = 1 sim_config_empty_local['grid_size'] = 1 client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty_local) assert grid.check_if_occupied(SquareGridCoordinate( x=0, y=0)) == True, 'should be occupied'
def test_breed(self): client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty) a_list = [(Animal.Fish, SquareGridCoordinate(x=1, y=1)), (Animal.Fish, SquareGridCoordinate(x=2, y=1)), (Animal.Fish, SquareGridCoordinate(x=3, y=1)), (Animal.Fish, SquareGridCoordinate(x=1, y=3)), (Animal.Fish, SquareGridCoordinate(x=3, y=2)), (Animal.Shark, SquareGridCoordinate(x=2, y=2))] # adding some food and a shark for t, c in a_list: client.init_animal(sim_id=grid._sid, current_turn=0, animal_type=t, coordinate=c) # updating grid turn grid._sim_turn = 4 shark_update = grid._eat() assert len(shark_update) == 1, 'Shark should have fed' breed_moved = grid._breed_and_move(fed_sharks=shark_update) assert len( breed_moved ) == 5, '4 fishes and one shark should have moved due to breeding' assert len(breed_moved) == (len(a_list) - 1) grid_df = grid.get_simulation_grid_data() assert len(grid_df[grid_df['animal_type'] == Animal.Shark]) == 2, 'Should be 2 Sharks' assert len(grid_df[grid_df['animal_type'] == Animal.Fish]) == 8, 'Should be 8 fishes'
def has_fish_in_square(self, sim_id: int, coordinates: List[SquareGridCoordinate]) -> List[SquareGridCoordinate]: """ Return a list of coordinate where fish are present :param sim_id: :param coordinates: :return: """ fish_df = self.get_animals_by_type(sim_id=sim_id, animal_type=Animal.Fish) has_fish = [] for coord in coordinates: fta = fish_df[(fish_df.coord_x == coord.x) & (fish_df.coord_y == coord.y)] if len(fta) > 0: has_fish.append(SquareGridCoordinate(int(fta.iloc[0].coord_x), int(fta.iloc[0].coord_y))) return has_fish
def _eat(self) -> Dict[int, SquareGridCoordinate]: """ Sharks that are adjacent to a Fish square eat and move into fish square (and do not move after) :return: list[(oid, prev_coordinate)] """ _debug = 'Turn: {:<3} - Eat - '.format(self._sim_turn) simulation_params = self.get_simulation_parameters(self._sid) # get a randomized df of all sharks sharks = self._persistence.get_animals_by_type( sim_id=self._sid, animal_type=Animal.Shark).sample(frac=1) sharks_eating = dict() shark_update = dict() for idx, shark in sharks.iterrows(): # get shark neighbour square shark_position = SquareGridCoordinate(shark.coord_x, shark.coord_y) shark_neighbour = square_grid_neighbours( simulation_params.grid_size, shark_position) # try to find fish has_fish = self._persistence.has_fish_in_square( sim_id=self._sid, coordinates=shark_neighbour) if len(has_fish) > 0: # Shark is eating random.shuffle(has_fish) eating_coord = has_fish[0] if self._persistence.eat_animal_in_square( sim_id=self._sid, coordinate=eating_coord): _logger.debug('{}Shark {} {} eat Fish {} and move'.format( _debug, shark.oid, shark_position, eating_coord)) # keep shark ref and position sharks_eating[shark.oid] = shark_position # move shark to eating position self._persistence.move_animal(sim_id=self._sid, animal_id=shark.oid, new_position=eating_coord) # add to update dictionary shark_update[shark.oid] = {'last_fed': self._sim_turn} else: raise ImpossibleAction( 'Something went wrong in Shark: {} feeding in {}'. format(shark, has_fish[0])) else: _logger.debug('{}turn: {}, No fish to eat for shark {}'.format( _debug, self._sim_turn, shark.oid)) self._persistence.update_animals(sim_id=self._sid, update_dict=shark_update) _logger.debug('{}{} sharks have eaten'.format(_debug, len(sharks_eating))) return sharks_eating
def test_animal_functions(self): client = SimulationClient('sqlite:///:memory:') # init DB sid = client.init_simulation(**sim_config) # add a bunch of animals for t, c in animal_list: client.init_animal(sim_id=sid, current_turn=0, animal_type=t, coordinate=c) # retrieve animals animals_df = client.get_animals_df(sim_id=1) assert isinstance(animals_df, pd.DataFrame), 'results is a DataFrame' assert len(animals_df) == 8, 'should be only 9 animals' # update animals # create update dict update_dict = { oid: { 'breed_count': 1, 'last_breed': 5, 'last_fed': 4 } for oid in animals_df.oid.values } client.update_animals(sim_id=sid, update_dict=update_dict) animals_df = client.get_animals_df(sim_id=1) assert animals_df.breed_count.unique( )[0] == 1, 'Breed count should be one now' client.update_animals(sim_id=sid, update_dict={1: {'alive': False}}) # this update should not work animal = client.get_animal(sim_id=sid, animal_id=1) assert animal.alive, 'Animal should still be alive' # but this should work client.kill_animal(sim_id=sid, animal_ids=[1, 2, 3]) animal = client.get_animal(sim_id=sid, animal_id=1) assert not animal.alive, 'This time alive was updates' # a dead animal does not occupy a square assert not client.coordinate_is_occupied( sim_id=sid, coordinate=SquareGridCoordinate(1, 3)) # test get animal by types shark_df = client.get_animals_by_type(sim_id=sid, animal_type=Animal.Shark) assert shark_df.animal_type.unique()[0] == Animal.Shark
def test_eating(self): client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty) a_list = [(Animal.Fish, SquareGridCoordinate(x=1, y=1)), (Animal.Fish, SquareGridCoordinate(x=2, y=1)), (Animal.Fish, SquareGridCoordinate(x=3, y=1)), (Animal.Fish, SquareGridCoordinate(x=1, y=3)), (Animal.Fish, SquareGridCoordinate(x=3, y=2)), (Animal.Shark, SquareGridCoordinate(x=2, y=2))] # adding some food and a shark for t, c in a_list: client.init_animal(sim_id=grid._sid, current_turn=0, animal_type=t, coordinate=c) # updating grid turn grid._sim_turn = 4 # AM: the next two rows are to allow optimized 'occupied coordinates' check grid.animals = grid.get_simulation_grid_data() grid.occupied_coord = set( zip(grid.animals.coord_x, grid.animals.coord_y)) shark_update = grid._eat() # shark has eaten shark = grid._persistence.get_animals_by_type( sim_id=grid._sid, animal_type=Animal.Shark).iloc[0] # the shark is in update list assert len( shark_update) == 1, 'There should be one shark in update list' assert shark.last_fed == 4, 'Shark last fed value should have updated' assert shark_update[shark.oid] == SquareGridCoordinate( x=2, y=2), 'Shark previous coordinate in shark update' # Fish is dead animal_in_square = client.get_animal_in_position( sim_id=grid._sid, coordinate=SquareGridCoordinate(int(shark.coord_x), int(shark.coord_y)), live_only=False) assert len( animal_in_square) == 2, 'there shoud be two animals in that square'
def test_moving_animals(self): client = SimulationClient('sqlite:///:memory:') # init DB sid = client.init_simulation(**sim_config) # add a bunch of animals for v in animal_list: client.init_animal(sim_id=sid, current_turn=0, animal_type=v[0], coordinate=v[1]) # test move assert not client.coordinate_is_occupied( sim_id=sid, coordinate=SquareGridCoordinate(5, 3)) client.move_animal(sim_id=sid, animal_id=4, new_position=SquareGridCoordinate(5, 3)) assert client.coordinate_is_occupied(sim_id=sid, coordinate=SquareGridCoordinate( 5, 3)) # trying to move dead animal client.kill_animal(sim_id=sid, animal_ids=[3]) with pytest.raises(ImpossibleAction): client.move_animal(sim_id=sid, animal_id=3, new_position=SquareGridCoordinate(3, 3)) # trying to move to an already occupied square with pytest.raises(NonEmptyCoordinate): client.move_animal(sim_id=sid, animal_id=5, new_position=SquareGridCoordinate(5, 3)) # but moving to a square occupied by a dead animal is possible assert not client.coordinate_is_occupied( sim_id=sid, coordinate=SquareGridCoordinate(3, 2)) client.move_animal(sim_id=sid, animal_id=1, new_position=SquareGridCoordinate(3, 2)) assert client.coordinate_is_occupied(sim_id=sid, coordinate=SquareGridCoordinate( 3, 2))
def test_fish_only(self): ''' Test if non-empty, but fish only: the simulation should end Make two cases: (1) add manually the fish to empty grid (2) use the config with updated value for corresponding key ''' # No. 1 client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty) a_list = [ (Animal.Fish, SquareGridCoordinate(x=1, y=1)), ] for t, c in a_list: client.init_animal(sim_id=grid._sid, current_turn=0, animal_type=t, coordinate=c) with pytest.raises(EndOfSimulatioError): grid._check_deads() with pytest.raises(EndOfSimulatioError): grid.play_turn() # repeat the same for No. 2 del client, grid, a_list sim_config_empty_local = copy.deepcopy(sim_config_empty) sim_config_empty_local['init_nb_fish'] = 1 client = SimulationClient('sqlite:///:memory:') grid = SimulationGrid(persistence=client, simulation_parameters=sim_config_empty_local) with pytest.raises(EndOfSimulatioError): grid._check_deads() with pytest.raises(EndOfSimulatioError): grid.play_turn()
def _move_animal_type(self, animal_type: Animal, already_moved: List[int]): """ Perform move action for a type of animal :param animal_type: :param already_moved: :return: """ _debug = 'Turn: {:<3} - Move - '.format(self._sim_turn) simulation_params = self.simulation_params animals = self._persistence.get_animals_by_type(sim_id=self._sid, animal_type=animal_type).sample(frac=1) for _, animal in animals.iterrows(): if animal.oid in already_moved: # this one has already moved so not moving _logger.debug('{}{} already moved'.format(_debug, animal.oid)) continue elif animal.spawn_turn == self._sim_turn: # fish was just spawn, not moving _logger.debug('{}{} just spawned'.format(_debug, animal.oid)) continue else: neighbors = square_grid_neighbours(simulation_params.grid_size, SquareGridCoordinate(animal.coord_x, animal.coord_y)) for neigh in neighbors: if not self.check_if_occupied(neigh): # move animal to this slot # set occupation flag to False occupation_flag = False _logger.debug('{}{} moved to {}'.format(_debug, animal_type.name, neigh)) coord_to_remove = self._persistence.move_animal(sim_id=self._sid, animal_id=animal.oid, new_position=neigh, occupied=occupation_flag) self.update_occupied_coord(old_coord=coord_to_remove, new_coord=(neigh.x, neigh.y)) # set back to None occupation_flag = None # break # AM: why we don't have break here?! Seems like we are making unnecessary operations else: _logger.debug('{}{}: {} had no space to move to'.format(_debug, animal_type.name, animal.oid)) return
def _move_animal_type(self, animal_type: Animal, already_moved: List[int]): """ Perform move action for a type of animal :param animal_type: :param already_moved: :return: """ _debug = 'Turn: {:<3} - Move - '.format(self._sim_turn) simulation_params = self.get_simulation_parameters(self._sid) animals = self._persistence.get_animals_by_type( sim_id=self._sid, animal_type=animal_type).sample(frac=1) for _, animal in animals.iterrows(): _logger.debug(animal) if animal.oid in already_moved: # this one has already moved so not moving _logger.debug('{}{} already moved'.format(_debug, animal.oid)) continue elif animal.spawn_turn == self._sim_turn: # fish was just spawn, not moving _logger.debug('{}{} just spawned'.format(_debug, animal.oid)) continue else: neighbors = square_grid_neighbours( simulation_params.grid_size, SquareGridCoordinate(animal.coord_x, animal.coord_y)) for neigh in neighbors: if not self._persistence.coordinate_is_occupied( self._sid, neigh): # move animal to this slot _logger.debug('{}{} oid[{}] moved to {}'.format( _debug, animal_type.name, animal.oid, neigh)) self._persistence.move_animal(sim_id=self._sid, animal_id=animal.oid, new_position=neigh) else: _logger.debug( '{}{}: {} had no space to move to'.format( _debug, animal_type.name, animal.oid)) return
def test_database_init(self): client = SimulationClient('sqlite:///:memory:') # init DB sid = client.init_simulation(**sim_config) assert len( client.get_all_simulations()) == 1, 'Should be only one simulation' assert isinstance(client.get_simulation(sid), Simulation) # check exception raised in init when adding animal on non-existent sim with pytest.raises(ValueError): client.init_animal(sim_id=10, current_turn=0, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(x=0, y=1)) # init some animals client.init_animal(sim_id=sid, current_turn=0, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(x=0, y=1)) assert client.coordinate_is_occupied(sim_id=sid, coordinate=SquareGridCoordinate( x=0, y=1)) # check exception for adding animal to an already occupied square with pytest.raises(NonEmptyCoordinate): client.init_animal(sim_id=sid, current_turn=0, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(x=0, y=1)) # but should be fine in a new simulation sid_2 = client.init_simulation(**sim_config) client.init_animal(sim_id=sid_2, current_turn=0, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(x=0, y=1)) # spawn outside the grid with pytest.raises(TopologyError): client.init_animal(sim_id=sid_2, current_turn=0, animal_type=Animal.Fish, coordinate=SquareGridCoordinate(x=10, y=1))
def test_animal_function(self): client = SimulationClient('sqlite:///:memory:') # init DB sid = client.init_simulation(**sim_config) # add a bunch of animals a_list = [(Animal.Fish, SquareGridCoordinate(x=1, y=1)), (Animal.Fish, SquareGridCoordinate(x=2, y=1)), (Animal.Fish, SquareGridCoordinate(x=3, y=1)), (Animal.Fish, SquareGridCoordinate(x=1, y=3)), (Animal.Fish, SquareGridCoordinate(x=3, y=2))] for t, c in a_list: client.init_animal(sim_id=sid, current_turn=0, animal_type=t, coordinate=c) coord_list = client.has_fish_in_square( sim_id=sid, coordinates=[SquareGridCoordinate(1, 1)]) assert len(coord_list) == 1, 'There should be a single fish' coord_list = client.has_fish_in_square( sim_id=sid, coordinates=[SquareGridCoordinate(1, 2)]) assert len(coord_list) == 0, 'There should be no fish here' neigh = square_grid_neighbours(grid_size=10, coordinate=SquareGridCoordinate(2, 2)) coord_list = client.has_fish_in_square(sim_id=sid, coordinates=neigh) assert len(coord_list) == 5, 'There should be 5 fishes here' # eating animals eaten = client.eat_animal_in_square(sim_id=sid, coordinate=SquareGridCoordinate( 1, 1)) assert eaten, 'Fish in 1, 1 should have been eaten' # can't eat dead Fish eaten = client.eat_animal_in_square(sim_id=sid, coordinate=SquareGridCoordinate( 1, 1)) assert not eaten, 'Should not be able to eat a dead Fish' client.init_animal(sim_id=sid, current_turn=0, animal_type=Animal.Shark, coordinate=SquareGridCoordinate(5, 5)) eaten = client.eat_animal_in_square(sim_id=sid, coordinate=SquareGridCoordinate( 5, 5)) assert not eaten, 'Should not be able to eat a Shark'
sim_config = { 'grid_size': 10, 'init_nb_fish': 50, 'fish_breed_maturity': 3, 'fish_breed_probability': 80, 'fish_speed': 2, 'init_nb_shark': 5, 'shark_breed_maturity': 5, 'shark_breed_probability': 100, 'shark_speed': 4, 'shark_starving': 4 } animal_list = [ (Animal.Fish, SquareGridCoordinate(x=1, y=3)), (Animal.Fish, SquareGridCoordinate(x=2, y=1)), (Animal.Fish, SquareGridCoordinate(x=3, y=2)), (Animal.Fish, SquareGridCoordinate(x=4, y=3)), (Animal.Fish, SquareGridCoordinate(x=5, y=4)), (Animal.Shark, SquareGridCoordinate(x=6, y=5)), (Animal.Shark, SquareGridCoordinate(x=6, y=6)), (Animal.Fish, SquareGridCoordinate(x=6, y=1)), ] class TestPersistence: def test_database_init(self): client = SimulationClient('sqlite:///:memory:') # init DB sid = client.init_simulation(**sim_config)
def _breed_and_move( self, fed_sharks: Dict[int, SquareGridCoordinate]) -> List[int]: """ Sharks or Fish that can breed, do so in same square (and Move), others moves if free space - Shark Breed first - Then Fish :parameter fed_sharks: list of sharks that fed and moved (breed, if possible, on previous position) :return: return the list of animals that bred and moved """ # perform breed for _debug = 'Turn: {:<3} - Breed - '.format(self._sim_turn) simulation_params = self.get_simulation_parameters(self._sid) moved = [] to_update = {} # First for sharks sharks = self._persistence.get_animals_by_type( sim_id=self._sid, animal_type=Animal.Shark).sample(frac=1) for idx, shark in sharks.iterrows(): # can shark breed? if (self._sim_turn - shark.spawn_turn ) >= simulation_params.shark_breed_maturity: # shark can breed if random.randint( 0, 100) <= simulation_params.shark_breed_probability: # shark is possibly breeding... breed_coord = None if shark.oid in fed_sharks: # ...if shark has eaten... breed_coord = fed_sharks[shark.oid] if self._persistence.coordinate_is_occupied( self._sid, breed_coord): # someone took that space before breeding _logger.debug( '{}This shark {} breeding has fed and moved,' + ' cannot breed in {} because position is taken' .format(_debug, shark.oid, breed_coord)) breed_coord = None _logger.debug( '{}This shark {} breeding has fed and moved, breeding in {}' .format(_debug, shark.oid, breed_coord)) # shark has already moved to eating position moved.append(shark.oid) else: # ... or if free space is available neighbors = square_grid_neighbours( simulation_params.grid_size, SquareGridCoordinate(shark.coord_x, shark.coord_y)) for neigh in neighbors: if not self._persistence.coordinate_is_occupied( self._sid, neigh): breed_coord = SquareGridCoordinate( int(shark.coord_x), int(shark.coord_y)) # move shark to this slot self._persistence.move_animal( sim_id=self._sid, animal_id=shark.oid, new_position=neigh) moved.append(shark.oid) _logger.debug( '{}Shark {} not fed breeding in {}, moving to {}' .format(_debug, shark.oid, breed_coord, neigh)) # break out of loop break if breed_coord is not None: to_update[shark.oid] = { 'last_breed': self._sim_turn, 'breed_count': shark.breed_count + 1 } # spawn new fish in breed_coord new_oid = self._persistence.init_animal( sim_id=self._sid, current_turn=self._sim_turn, animal_type=Animal.Shark, coordinate=breed_coord) _logger.debug('{}Spawning new shark {} {}'.format( _debug, new_oid, breed_coord)) # Last Fishes, randomize fishes = self._persistence.get_animals_by_type( sim_id=self._sid, animal_type=Animal.Fish).sample(frac=1) for idx, fish in fishes.iterrows(): # can fish breed? if (self._sim_turn - fish.spawn_turn) >= simulation_params.fish_breed_maturity: # fish can breed if random.randint( 0, 100) <= simulation_params.fish_breed_probability: # fish is possibly breeding if free space is available breed_coord = SquareGridCoordinate(int(fish.coord_x), int(fish.coord_y)) _logger.debug( '{}Fish breeding in {} if space is available'.format( _debug, breed_coord)) neighbors = square_grid_neighbours( simulation_params.grid_size, SquareGridCoordinate(fish.coord_x, fish.coord_y)) for neigh in neighbors: if not self._persistence.coordinate_is_occupied( self._sid, neigh): _logger.debug( '{}Space found in {}, fish breed and move'. format(_debug, neigh)) to_update[fish.oid] = { 'last_breed': self._sim_turn, 'breed_count': fish.breed_count + 1 } # move fish to this slot self._persistence.move_animal(sim_id=self._sid, animal_id=fish.oid, new_position=neigh) moved.append(fish.oid) # spawn new fish in breed_coord self._persistence.init_animal( sim_id=self._sid, current_turn=self._sim_turn, animal_type=Animal.Fish, coordinate=breed_coord) # break out of loop break # now, update all animals if len(to_update) > 0: _logger.debug('{}{} animals updated after breeding'.format( _debug, len(to_update))) self._persistence.update_animals(sim_id=self._sid, update_dict=to_update) # add shark that ate and did not breed to the moved list for oid in fed_sharks.keys(): if oid not in moved: _logger.debug('{}Shark {} did not breed after movin'.format( _debug, oid)) moved.append(oid) # return animal list that have already bred and moved return moved
from fish_bowl.dataio.persistence import SimulationClient, get_database_string from fish_bowl.process.topology import SquareGridCoordinate from fish_bowl.process.simple_display import display_simple_grid client = SimulationClient(get_database_string()) print(display_simple_grid(client.get_animals_df(sim_id=1), 10)) coord_1 = SquareGridCoordinate(6, 8) coord_2 = SquareGridCoordinate(7, 9) print( client.get_animal_in_position(sim_id=1, coordinate=coord_1, live_only=False)) print( client.get_animal_in_position(sim_id=1, coordinate=coord_2, live_only=False))