def test_obj_exists_handles_out_of_grid_positions(data: st.DataObject) -> None:
    """ Make sure the correct error is raised. """
    raised_index_error = False
    raised_value_error = False
    index_error = None
    value_error = None
    env = data.draw(bst.envs())
    obj_type_id = data.draw(bst.obj_type_ids(env=env))
    pos_indices = st.integers(min_value=-100, max_value=100)
    pos_strategy = st.tuples(pos_indices, pos_indices)
    pos: Tuple[int, int] = data.draw(pos_strategy)  # type: ignore
    try:
        existence = env._obj_exists(obj_type_id, pos)
    except IndexError as err:
        raised_index_error = True
        index_error = err
    except ValueError as err:
        raised_value_error = True
        value_error = err

    # If pos in in the grid, should raise no errors.
    if 0 <= pos[0] < env.width and 0 <= pos[1] < env.height:
        try:
            assert not raised_index_error
        except AssertionError:
            raise index_error  # type: ignore

    # If negative, should catch and raise a ValueError.
    elif pos[0] < 0 or pos[1] < 0:
        assert raised_value_error

    # Otherwise, currently raises an IndexError.
    # TODO: Add an error message for this?
    else:
        assert raised_index_error
Exemple #2
0
def test_move_correctly_modifies_agent_state(data: st.DataObject) -> None:
    """ Makes sure they actually move or STAY. """
    # TODO: Handle out-of-bounds errors.
    # TODO: Consider making the environment toroidal.
    env = data.draw(bst.envs())
    env.reset()
    old_locations: Dict[int, Tuple[int, int]] = {}
    for agent_id, agent in env.agents.items():
        old_locations[agent_id] = agent.pos
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))
    executed_dict = env._move(tuple_action_dict)

    # TODO: Consider making ``env.LEFT``, etc tuples which can be added to existing
    # positions rather than just integers.
    for agent_id, action in executed_dict.items():
        agent = env.agents[agent_id]
        move = action[0]
        old_pos = old_locations[agent_id]
        if move == env.UP or move == env.DOWN:
            assert agent.pos[0] == old_pos[0]
        if move == env.LEFT or move == env.RIGHT:
            assert agent.pos[1] == old_pos[1]
        if move == env.UP:
            assert agent.pos[1] == old_pos[1] + 1
        if move == env.DOWN:
            assert agent.pos[1] == old_pos[1] - 1
        if move == env.RIGHT:
            assert agent.pos[0] == old_pos[0] + 1
        if move == env.LEFT:
            assert agent.pos[0] == old_pos[0] - 1
        if move == env.STAY:
            assert agent.pos == old_pos
class EnvironmentMachine(RuleBasedStateMachine):
    """ Finite-state machine for testing ``Env`` multi agent environment. """
    @timing
    @given(env=bst.envs())
    def __init__(self, env: Env):
        super(EnvironmentMachine, self).__init__()
        self.env = env

    @initialize()
    @timing
    def reset(self) -> None:
        env = self.env
        obs = env.reset()
        for agent_id, agent_obs in obs.items():

            # Calculate correct number of each object type.
            correct_obj_nums = {
                obj_type: 0
                for obj_type in env.obj_type_ids.values()
            }
            for dx, dy in product(range(-env.sight_len, env.sight_len + 1),
                                  repeat=2):
                x = env.agents[agent_id].pos[0] + dx
                y = env.agents[agent_id].pos[1] + dy
                if (x, y) not in product(range(env.width), range(env.height)):
                    continue
                for obj_type in env.obj_type_ids.values():
                    correct_obj_nums[obj_type] += int(env.grid[x][y][obj_type])

            # Calculate number of each object type in returned observations.
            observed_obj_nums = {
                obj_type: 0
                for obj_type in env.obj_type_ids.values()
            }
            for dx, dy in product(range(-env.sight_len, env.sight_len + 1),
                                  repeat=2):
                for obj_type in env.obj_type_ids.values():
                    observed_obj_nums[obj_type] += int(
                        agent_obs[obj_type][dx][dy])

            assert correct_obj_nums == observed_obj_nums

    @rule()
    @timing
    @given(data=st.data())
    def update_pos(self, data: st.DataObject) -> None:
        pos = data.draw(bst.positions(env=self.env))
        move = data.draw(bst.moves(env=self.env))
        new_pos = self.env._update_pos(pos, move)

        if pos[0] != new_pos[0]:
            assert pos[1] == new_pos[1]
            assert abs(pos[0] - new_pos[0]) == 1
        if pos[1] != new_pos[1]:
            assert pos[0] == new_pos[0]
            assert abs(pos[1] - new_pos[1]) == 1

    @rule()
    def dummy(self) -> None:
        assert True
def test_consume_removes_nothing_else(data: st.DataObject) -> None:
    """ Otherwise, food should remain in place. """
    env = data.draw(bst.envs())
    env.reset()
    food_obj_type_id = env.obj_type_ids["food"]
    agent_food_positions: Dict[int, Tuple[int, int]] = {}
    for agent_id, agent in env.agents.items():
        if env.grid[agent.pos + (food_obj_type_id,)] == 1:
            agent_food_positions[agent_id] = agent.pos
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))

    eating_positions: List[Tuple[int, int]] = []
    for agent_id, pos in agent_food_positions.items():
        if tuple_action_dict[agent_id][1] == env.EAT:
            eating_positions.append(pos)

    food_positions: Set[Tuple[int, int]] = set()
    for x in range(env.width):
        for y in range(env.height):
            if env.grid[(x, y) + (food_obj_type_id,)] == 1:
                food_positions.add((x, y))
    persistent_food_positions = food_positions - set(eating_positions)

    env._consume(tuple_action_dict)

    for pos in persistent_food_positions:
        assert env.grid[pos + (food_obj_type_id,)] == 1
Exemple #5
0
def test_env_plant_does_nothing_when_zero_prob(data: st.DataObject) -> None:
    env: Env = data.draw(bst.envs())
    env.adaptive_food = False
    env.food_regen_prob = 0.0
    old_num_foods = env.num_foods
    env._plant()
    assert env.num_foods == old_num_foods
Exemple #6
0
def test_get_obs_has_correct_shape(data: st.DataObject) -> None:
    """ Make sure that a returned observation has the correct shape. """
    env = data.draw(bst.envs())
    env.reset()
    pos = data.draw(bst.positions(env=env))
    ob = env._get_obs(pos)
    assert ob.shape == env.observation_space.shape
Exemple #7
0
def test_mate_makes_num_agents_nondecreasing(data: st.DataObject) -> None:
    """ Makes sure ``len(agents)`` is nondecreasing. """
    env = data.draw(bst.envs())
    env.reset()
    old_num_agents = len(env.agents)
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))
    env._mate(tuple_action_dict)
    assert old_num_agents <= len(env.agents)
Exemple #8
0
def test_mate_adds_children_to_agents(data: st.DataObject) -> None:
    """ Makes sure child ids get added to ``env.agents``. """
    env = data.draw(bst.envs())
    env.reset()
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))
    child_ids = env._mate(tuple_action_dict)
    for child_id in child_ids:
        assert child_id in env.agents
Exemple #9
0
def test_get_obs_has_no_out_of_range_elements(data: st.DataObject) -> None:
    """ Make sure that a returned observation only contains 0 or 1. """
    env = data.draw(bst.envs())
    env.reset()
    pos = data.draw(bst.positions(env=env))
    ob = env._get_obs(pos)
    for elem in np.nditer(ob):
        assert elem in (0.0, 1.0)
Exemple #10
0
def test_mate_children_are_new(data: st.DataObject) -> None:
    """ Makes sure children are new. """
    env = data.draw(bst.envs())
    env.reset()
    old_agent_memory_addresses = [id(agent) for agent in env.agents.values()]
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))
    child_ids = env._mate(tuple_action_dict)
    for child_id in child_ids:
        assert id(env.agents[child_id]) not in old_agent_memory_addresses
Exemple #11
0
def test_move_holds_other_actions_invariant(data: st.DataObject) -> None:
    """ Makes sure the returned action dict only modifies move subaction space. """
    env = data.draw(bst.envs())
    env.reset()

    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))
    executed_dict = env._move(tuple_action_dict)

    pairs = zip(list(tuple_action_dict.values()), list(executed_dict.values()))
    for attempted_action, executed_action in pairs:
        assert attempted_action[1:] == executed_action[1:]
Exemple #12
0
def test_move_only_changes_to_stay(data: st.DataObject) -> None:
    """ Makes sure the returned action dict only changes to STAY if at all. """
    env = data.draw(bst.envs())
    env.reset()

    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))
    executed_dict = env._move(tuple_action_dict)

    pairs = zip(list(tuple_action_dict.values()), list(executed_dict.values()))
    for attempted_action, executed_action in pairs:
        if attempted_action[0] != executed_action[0]:
            assert executed_action[0] == env.STAY
def test_obj_exists_marks_grid_squares_correctly(data: st.DataObject) -> None:
    """ Make sure emptiness of grid matches function output. """
    env = data.draw(bst.envs())
    env.reset()
    obj_type_id = data.draw(bst.obj_type_ids(env=env))

    for x in range(env.width):
        for y in range(env.height):
            if env.grid[x][y][obj_type_id] == 0:
                assert not env._obj_exists(obj_type_id, (x, y))
            else:
                assert env._obj_exists(obj_type_id, (x, y))
def test_obj_exists_has_correct_num_agents(data: st.DataObject) -> None:
    """ Make sure iterating over grid with obj_exists sees all agents. """
    env = data.draw(bst.envs())
    env.reset()
    obj_type_id = env.obj_type_ids["agent"]

    num_agents = 0
    for x in range(env.width):
        for y in range(env.height):
            if env._obj_exists(obj_type_id, (x, y)):
                num_agents += 1
    assert num_agents == len(env.agents)
Exemple #15
0
def test_env_place_no_double_place_homo(data: st.DataObject) -> None:
    """ Tests that env gets angry if you try to double up h**o objs. """
    env = data.draw(strategies.envs())
    pos = data.draw(strategies.positions(env=env))
    env.reset()
    homo_obj_type_ids = set(
        env.obj_type_ids.values()) - env.heterogeneous_obj_type_ids
    obj_type_id = data.draw(st.sampled_from(list(homo_obj_type_ids)))

    if not env._obj_exists(obj_type_id, pos):
        env._place(obj_type_id, pos)
        with pytest.raises(ValueError):
            env._place(obj_type_id, pos)
Exemple #16
0
def test_consume_makes_agent_health_nondecreasing(data: st.DataObject) -> None:
    """ Tests that agent.health in the correct direction. """
    env = data.draw(bst.envs())
    env.reset()
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))

    old_healths: Dict[int, float] = {}
    for agent_id, agent in env.agents.items():
        old_healths[agent_id] = agent.health

    env._consume(tuple_action_dict)

    for agent_id, agent in env.agents.items():
        assert old_healths[agent_id] <= agent.health
Exemple #17
0
def test_get_obs_has_correct_objects(data: st.DataObject) -> None:
    """ Make sure that a returned observation is accurate w.r.t. ``env.grid``. """
    env = data.draw(bst.envs())
    env.reset()
    pos = data.draw(bst.positions(env=env))
    ob = env._get_obs(pos)
    for i in range(ob.shape[1]):
        for j in range(ob.shape[2]):
            ob_pos = (pos[0] + i - env.sight_len, pos[1] + j - env.sight_len)
            if 0 <= ob_pos[0] < env.width and 0 <= ob_pos[1] < env.height:
                ob_square = ob[:, i, j]
                env_square = env.grid[ob_pos]
                assert np.all(ob_square == env_square)
            else:
                assert np.all(ob[:, i, j] == np.zeros((env.num_obj_types,)))
Exemple #18
0
def test_mate_executes_action(data: st.DataObject) -> None:
    """ Tests children are created when they're suppsed to. """
    env = data.draw(bst.envs())

    assume(env.height * env.width >= 3)

    # Generate two adjacent positions.
    mom_pos = data.draw(bst.positions(env=env))
    open_positions = env._get_adj_positions(mom_pos)
    dad_pos = data.draw(st.sampled_from(open_positions))

    # Create a mom and dad.
    mom = Agent(
        config=env.config, num_actions=env.num_actions, pos=mom_pos, initial_health=1.0,
    )
    dad = Agent(
        config=env.config, num_actions=env.num_actions, pos=dad_pos, initial_health=1.0,
    )
    mom.is_mature = True
    dad.is_mature = True
    mom.mating_cooldown = 0
    dad.mating_cooldown = 0
    mom_id = env._new_agent_id()
    dad_id = env._new_agent_id()
    env.agents[mom_id] = mom
    env.agents[dad_id] = dad
    env._place(env.obj_type_ids["agent"], mom_pos, mom_id)
    env._place(env.obj_type_ids["agent"], dad_pos, dad_id)

    # Construct subactions.
    mom_move = data.draw(bst.moves(env=env))
    dad_move = data.draw(bst.moves(env=env))
    mom_consumption = data.draw(bst.consumptions(env=env))
    dad_consumption = data.draw(bst.consumptions(env=env))
    mom_action = (mom_move, mom_consumption, env.MATE)
    dad_action = (dad_move, dad_consumption, env.MATE)

    action_dict = {mom_id: mom_action, dad_id: dad_action}
    child_ids = env._mate(action_dict)
    assert len(child_ids) == 1
    child = env.agents[child_ids.pop()]

    def adjacent(pos1: Tuple[int, int], pos2: Tuple[int, int]) -> bool:
        """ Decide whether or not two positions are orthogonally adjacent. """
        return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1]) == 1

    assert len(env.agents) == 3
    assert adjacent(child.pos, mom.pos) or adjacent(child.pos, dad.pos)
Exemple #19
0
def test_consume_decreases_num_foods_correctly(data: st.DataObject) -> None:
    """ The ``env.num_foods`` attribute is decremented properly. """
    env = data.draw(bst.envs())
    env.reset()
    food_obj_type_id = env.obj_type_ids["food"]
    agent_food_positions: Dict[int, Tuple[int, int]] = {}
    for agent_id, agent in env.agents.items():
        if env.grid[agent.pos + (food_obj_type_id,)] == 1:
            agent_food_positions[agent_id] = agent.pos
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))

    eating_positions: List[Tuple[int, int]] = []
    for agent_id, pos in agent_food_positions.items():
        if tuple_action_dict[agent_id][1] == env.EAT:
            eating_positions.append(pos)

    old_num_foods = env.num_foods
    env._consume(tuple_action_dict)
    assert old_num_foods - env.num_foods == len(eating_positions)
def test_obj_exists_detects_bad_id_map(data: st.DataObject) -> None:
    """ Make sure error is raised when ``id_map`` disagrees with ``grid``. """
    env = data.draw(bst.envs())
    obj_type_id = env.obj_type_ids["agent"]
    pos = data.draw(bst.positions(env=env))
    x = pos[0]
    y = pos[1]
    grid_idx = pos + (obj_type_id, )
    singleton_id_set = set([data.draw(st.integers(min_value=0, max_value=3))])
    env.id_map[x][y][obj_type_id] = singleton_id_set
    raised_value_error = False
    try:
        _ = env._obj_exists(obj_type_id, pos)
    except ValueError:
        raised_value_error = True
    if env.grid[grid_idx] == 0:
        assert raised_value_error
    else:
        assert not raised_value_error
Exemple #21
0
def test_consume_removes_food_when_appropriate(data: st.DataObject) -> None:
    """ If the action is ``EAT`` and there's food, should disappear. """
    env = data.draw(bst.envs())
    env.reset()
    food_obj_type_id = env.obj_type_ids["food"]
    agent_food_positions: Dict[int, Tuple[int, int]] = {}
    for agent_id, agent in env.agents.items():
        if env.grid[agent.pos + (food_obj_type_id,)] == 1:
            agent_food_positions[agent_id] = agent.pos
    tuple_action_dict = data.draw(bst.tuple_action_dicts(env=env))

    eating_positions: List[Tuple[int, int]] = []
    for agent_id, pos in agent_food_positions.items():
        if tuple_action_dict[agent_id][1] == env.EAT:
            eating_positions.append(pos)

    env._consume(tuple_action_dict)

    for pos in eating_positions:
        assert env.grid[pos + (food_obj_type_id,)] == 0
def test_obj_exists_handles_invalid_obj_type_ids(data: st.DataObject) -> None:
    """ Make sure the correct error is raised. """
    env = data.draw(bst.envs())
    obj_type_id = data.draw(st.integers(min_value=-10, max_value=10))
    pos = data.draw(bst.positions(env=env))
    raised_value_error = False
    try:
        existence = env._obj_exists(obj_type_id, pos)
    except ValueError as err:
        raised_value_error = True
        value_error = err

    # If the obj_type_id is invalid, should raise ValueError.
    if obj_type_id not in env.obj_type_names:
        assert raised_value_error

    # Otherwise, should raise no error.
    else:
        try:
            assert not raised_value_error
        except AssertionError:
            raise value_error
Exemple #23
0
def test_env_plant_fills_grid_when_one_prob(data: st.DataObject) -> None:
    env: Env = data.draw(bst.envs())
    env.adaptive_food = False
    env.food_regen_prob = 1.0
    env._plant()
    assert env.num_foods == env.width * env.height
Exemple #24
0
def test_env_plant_doesnt_remove_food(data: st.DataObject) -> None:
    env: Env = data.draw(bst.envs())
    old_num_foods = env.num_foods
    env._plant()
    assert env.num_foods >= old_num_foods
Exemple #25
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Test that ``Env.fill()`` works correctly. """
import itertools

from hypothesis import given, settings
from hypothesis import HealthCheck as hc

from bees.env import Env
from bees.tests import strategies

# pylint: disable=no-value-for-parameter


@given(strategies.envs())
def test_env_fill_places_correct_number_of_agents(env: Env) -> None:
    """ Tests that we find a place for each agent in ``self.agents``. """
    env.fill()
    num_grid_agents = 0
    for row in env.grid:
        for square in row:
            if square[env.obj_type_ids["agent"]] == 1:
                num_grid_agents += 1
    assert num_grid_agents == len(env.agents)


@settings(suppress_health_check=[hc.too_slow])
@given(strategies.envs())
def test_env_fill_sets_all_agent_positions_correctly(env: Env) -> None:
    """ Tests that ``agent.pos`` is set correctly. """
    env.fill()