示例#1
0
def test_rng_if_none():
    # default call returns library rng
    rng = None
    assert get_gv_rng_if_none(rng) is get_gv_rng()

    # call with an rng returns that rng
    rng = make_rng()
    assert get_gv_rng_if_none(rng) is rng
示例#2
0
def stochastic_raytracing_visibility(  # TODO add test
    grid: Grid,
    position: Position,
    *,
    rng: Optional[rnd.Generator] = None,
) -> np.ndarray:
    rng = get_gv_rng_if_none(rng)

    area = Area((0, grid.height - 1), (0, grid.width - 1))
    rays = cached_compute_rays_fancy(position, area)

    counts_num = np.zeros((area.height, area.width), dtype=int)
    counts_den = np.zeros((area.height, area.width), dtype=int)

    for ray in rays:
        light = True
        for pos in ray:
            if light:
                counts_num[pos.y, pos.x] += 1

            counts_den[pos.y, pos.x] += 1

            light = light and grid[pos].transparent

    probs = np.nan_to_num(counts_num / counts_den)
    visibility = probs <= rng.random(probs.shape)
    return visibility
def creeping_walls(
    state: State,
    action: Action,  # pylint: disable=unused-argument
    *,
    rng: Optional[rnd.Generator] = None,
) -> None:
    """randomly chooses a Floor tile and turns it into a Wall tile"""

    rng = get_gv_rng_if_none(rng)  # necessary to use rng object!

    # all positions associated with a Floor object
    floor_positions = [
        position for position in state.grid.positions()
        if isinstance(state.grid[position], Floor)
    ]

    try:
        # floor_positions could be an empty list
        position = rng.choice(floor_positions)
    except ValueError:
        # there are no floor positions
        pass
    else:
        # if we were able to sample a position, change the corresponding Floor
        # into a Wall
        state.grid[position] = Wall()
示例#4
0
def reset_teleport(height: int,
                   width: int,
                   *,
                   rng: Optional[rnd.Generator] = None) -> State:

    rng = get_gv_rng_if_none(rng)

    state = reset_empty(height, width)
    assert isinstance(state.grid[height - 2, width - 2], Goal)

    # Place agent on top left
    state.agent.position = (1, 1)  # type: ignore
    state.agent.orientation = rng.choice([Orientation.E, Orientation.S])

    num_telepods = 2
    telepods = [Telepod(Color.RED) for _ in range(num_telepods)]
    positions = rng.choice(
        [
            position for position in state.grid.positions()
            if isinstance(state.grid[position], Floor)
            and position != state.agent.position
        ],
        size=num_telepods,
        replace=False,
    )
    for position, telepod in zip(positions, telepods):
        state.grid[position] = telepod

    # Place agent on top left
    state.agent.position = (1, 1)  # type: ignore
    state.agent.orientation = rng.choice([Orientation.E, Orientation.S])

    return state
示例#5
0
def match_key_color(
        *,
        rng: Optional[rnd.Generator] = None,  # pylint: disable=unused-argument
) -> State:
    """the agent has to pick the correct key to open a randomly colored door"""

    rng = get_gv_rng_if_none(rng)  # necessary to use rng object!

    # only consider these colors
    colors = [Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW]
    # randomly choose location of keys
    key1, key2, key3, key4 = rng.permute([Key(color) for color in colors])
    # randomly choose color of door
    door = Door(Door.Status.LOCKED, rng.choice(colors))

    # grids can be constructed directly from objects
    grid = Grid.from_objects([
        [Wall(), Wall(), Wall(), Wall(),
         Wall()],
        [Wall(), Wall(), Goal(), Wall(),
         Wall()],
        [Wall(), Wall(), door, Wall(), Wall()],
        [Wall(), key1, Floor(), key2, Wall()],
        [Wall(), key3, Floor(), key4, Wall()],
        [Wall(), Wall(), Wall(), Wall(),
         Wall()],
    ])

    # positioning the agent in the above grid
    agent = Agent((4, 2), Orientation.N)

    return State(grid, agent)
示例#6
0
def reset_keydoor(height: int,
                  width: int,
                  *,
                  rng: Optional[rnd.Generator] = None) -> State:
    """An environment with a key and a door

    Creates a height x width (including outer walls) grid with a random column
    of walls. The agent and a yellow key are randomly dropped left of the
    column, while the goal is placed in the bottom right. For example::

        #########
        # @#    #
        #  D    #
        #K #   G#
        #########

    Args:
        height (`int`):
        width (`int`):
        rng: (`Generator, optional`)

    Returns:
        State:
    """
    if height < 3 or width < 5 or (height, width) == (3, 5):
        raise ValueError(
            f'Shape must larger than (3, 5), given {(height, width)}')

    rng = get_gv_rng_if_none(rng)

    state = reset_empty(height, width)
    assert isinstance(state.grid[height - 2, width - 2], Goal)

    # Generate vertical splitting wall
    x_wall = rng.integers(2, width - 3, endpoint=True)
    line_wall = draw_line_vertical(state.grid, range(1, height - 1), x_wall,
                                   Wall)

    # Place yellow, locked door
    pos_wall = rng.choice(line_wall)
    state.grid[pos_wall] = Door(Door.Status.LOCKED, Color.YELLOW)

    # Place yellow key left of wall
    # XXX: potential general function
    y_key = rng.integers(1, height - 2, endpoint=True)
    x_key = rng.integers(1, x_wall - 1, endpoint=True)
    state.grid[y_key, x_key] = Key(Color.YELLOW)

    # Place agent left of wall
    # XXX: potential general function
    y_agent = rng.integers(1, height - 2, endpoint=True)
    x_agent = rng.integers(1, x_wall - 1, endpoint=True)
    state.agent.position = (y_agent, x_agent)  # type: ignore
    state.agent.orientation = rng.choice(list(Orientation))

    return state
def random_transition(
    state: State,
    action: Action,
    *,
    transition_function: TransitionFunction,
    p_success: float,
    rng: Optional[rnd.Generator] = None,
) -> None:
    """randomly determines whether to perform a transition or not"""

    rng = get_gv_rng_if_none(rng)  # necessary to use rng object!

    # flip coin to see whether to run the transition_function
    success = rng.random() <= p_success
    if success:
        transition_function(state, action, rng=rng)
def coinflip_visibility(
    grid: Grid,
    position: Position,
    *,
    rng: Optional[rnd.Generator] = None,
) -> np.ndarray:
    """randomly determines tile visibility"""

    rng = get_gv_rng_if_none(rng)  # necessary to use rng object!

    # sample a random binary visibility matrix
    visibility = rng.integers(2, size=grid.shape).astype(bool)

    # the agent tile should always be visible regardless
    visibility[position.y, position.x] = True

    return visibility
示例#9
0
def reset_dynamic_obstacles(
    height: int,
    width: int,
    num_obstacles: int,
    random_agent_pos: bool = False,
    *,
    rng: Optional[rnd.Generator] = None,
) -> State:
    """An environment with dynamically moving obstacles

    Args:
        height (`int`): height of grid
        width (`int`): width of grid
        num_obstacles (`int`): number of dynamic obstacles
        random_agent (`bool, optional`): position of agent, in corner if False
        rng: (`Generator, optional`)

    Returns:
        State:
    """

    rng = get_gv_rng_if_none(rng)

    state = reset_empty(height, width, random_agent_pos, rng=rng)
    vacant_positions = [
        position for position in state.grid.positions()
        if isinstance(state.grid[position], Floor)
        and position != state.agent.position
    ]

    try:
        sample_positions = rng.choice(vacant_positions,
                                      size=num_obstacles,
                                      replace=False)
    except ValueError as e:
        raise ValueError(
            f'Too many obstacles ({num_obstacles}) and not enough '
            f'vacant positions ({len(vacant_positions)})') from e

    for pos in sample_positions:
        assert isinstance(state.grid[pos], Floor)
        state.grid[pos] = MovingObstacle()

    return state
示例#10
0
def step_telepod(
        state: State,
        action: Action,  # pylint: disable=unused-argument
        *,
        rng: Optional[rnd.Generator] = None,  # pylint: disable=unused-argument
) -> None:
    """Teleports the agent if positioned on the telepod"""

    rng = get_gv_rng_if_none(rng)

    telepod = state.grid[state.agent.position]

    if isinstance(telepod, Telepod):
        positions = [
            position for position in state.grid.positions()
            if position != state.agent.position
            and isinstance(state.grid[position], Telepod)
            and state.grid[position].color == telepod.color
        ]
        state.agent.position = rng.choice(positions)
示例#11
0
def reset_empty(
    height: int,
    width: int,
    random_agent: bool = False,
    random_goal: bool = False,
    *,
    rng: Optional[rnd.Generator] = None,
) -> State:
    """An empty environment"""

    if height < 4 or width < 4:
        raise ValueError('height and width need to be at least 4')

    rng = get_gv_rng_if_none(rng)

    # TODO test creation (e.g. count number of walls, goals, check held item)

    grid = Grid(height, width)
    draw_wall_boundary(grid)

    if random_goal:
        goal_y = rng.integers(1, height - 2, endpoint=True)
        goal_x = rng.integers(1, width - 2, endpoint=True)
    else:
        goal_y = height - 2
        goal_x = width - 2

    grid[goal_y, goal_x] = Goal()

    if random_agent:
        agent_position = rng.choice([
            position for position in grid.positions()
            if isinstance(grid[position], Floor)
        ])
        agent_orientation = rng.choice(list(Orientation))
    else:
        agent_position = (1, 1)
        agent_orientation = Orientation.E

    agent = Agent(agent_position, agent_orientation)
    return State(grid, agent)
示例#12
0
def _step_moving_obstacle(grid: Grid,
                          position: Position,
                          *,
                          rng: Optional[rnd.Generator] = None):
    """Utility for moving a single MovingObstacle randomly"""
    assert isinstance(grid[position], MovingObstacle)

    rng = get_gv_rng_if_none(rng)

    next_positions = [
        next_position
        for next_position in get_manhattan_boundary(position, distance=1)
        if next_position in grid and isinstance(grid[next_position], Floor)
    ]

    try:
        next_position = rng.choice(next_positions)
    except ValueError:
        pass
    else:
        grid.swap(position, next_position)
示例#13
0
def step_moving_obstacles(
    state: State,
    action: Action,
    *,
    rng: Optional[rnd.Generator] = None,
) -> None:
    """Moves moving obstacles randomly

    Moves each MovingObstacle only to cells containing _Floor_ objects, and
    will do so with random walk. In current implementation can only move 1 cell
    non-diagonally. If (and only if) no open cells are available will it stay
    put

    Args:
        state (`State`): current state
        action (`Action`): action taken by agent (ignored)
    """
    rng = get_gv_rng_if_none(rng)

    for position, obj in _unique_object_type_positions(state.grid,
                                                       MovingObstacle):
        _step_moving_obstacle(state.grid, position, rng=rng)
示例#14
0
def reset_rooms(  # pylint: disable=too-many-locals
    height: int,
    width: int,
    layout: Tuple[int, int],
    *,
    rng: Optional[rnd.Generator] = None,
) -> State:

    rng = get_gv_rng_if_none(rng)

    # TODO test creation (e.g. count number of walls, goals, check held item)

    layout_height, layout_width = layout

    y_splits = np.linspace(
        0,
        height - 1,
        num=layout_height + 1,
        dtype=int,
    )

    if len(y_splits) != len(set(y_splits)):
        raise ValueError(
            f'insufficient height ({height}) for layout ({layout})')

    x_splits = np.linspace(
        0,
        width - 1,
        num=layout_width + 1,
        dtype=int,
    )

    if len(x_splits) != len(set(x_splits)):
        raise ValueError(
            f'insufficient width ({height}) for layout ({layout})')

    grid = Grid(height, width)
    draw_room_grid(grid, y_splits, x_splits, Wall)

    # passages in horizontal walls
    for y in y_splits[1:-1]:
        for x_from, x_to in mitt.pairwise(x_splits):
            x = rng.integers(x_from + 1, x_to)
            grid[y, x] = Floor()

    # passages in vertical walls
    for y_from, y_to in mitt.pairwise(y_splits):
        for x in x_splits[1:-1]:
            y = rng.integers(y_from + 1, y_to)
            grid[y, x] = Floor()

    # sample agent and goal positions
    agent_position, goal_position = rng.choice(
        [
            position for position in grid.positions()
            if isinstance(grid[position], Floor)
        ],
        size=2,
        replace=False,
    )
    agent_orientation = rng.choice(list(Orientation))

    grid[goal_position] = Goal()
    agent = Agent(agent_position, agent_orientation)
    return State(grid, agent)
示例#15
0
def reset_crossing(  # pylint: disable=too-many-locals
    height: int,
    width: int,
    num_rivers: int,
    object_type: Type[GridObject],
    *,
    rng: Optional[rnd.Generator] = None,
) -> State:
    """An environment with "rivers" to be crosses

    Creates a height x width (including wall) grid with random rows/columns of
    objects called "rivers". The agent needs to navigate river openings to
    reach the goal.  For example::

        #########
        #@    # #
        #### ####
        #     # #
        ## ######
        #       #
        #     # #
        #     #G#
        #########

    Args:
        height (`int`): odd height of grid
        width (`int`): odd width of grid
        num_rivers (`int`): number of `rivers`
        object_type (`Type[GridObject]`): river's object type
        rng: (`Generator, optional`)

    Returns:
        State:
    """
    if height < 5 or height % 2 == 0:
        raise ValueError(
            f"Crossing environment height must be odd and >= 5, given {height}"
        )

    if width < 5 or width % 2 == 0:
        raise ValueError(
            f"Crossing environment width must be odd and >= 5, given {width}")

    if num_rivers < 0:
        raise ValueError(
            f"Crossing environment number of walls must be >= 0, given {height}"
        )

    rng = get_gv_rng_if_none(rng)

    state = reset_empty(height, width)
    assert isinstance(state.grid[height - 2, width - 2], Goal)

    # token `horizontal` and `vertical` objects
    h, v = object(), object()

    # all rivers specified by orientation and position
    rivers = list(
        itt.chain(
            ((h, i) for i in range(2, height - 2, 2)),
            ((v, j) for j in range(2, width - 2, 2)),
        ))

    # sample subset of random rivers
    rng.shuffle(rivers)  # NOTE: faster than rng.choice
    rivers = rivers[:num_rivers]

    # create horizontal rivers without crossings
    rivers_h = sorted([pos for direction, pos in rivers if direction is h])
    for y in rivers_h:
        draw_line_horizontal(state.grid, y, range(1, width - 1), object_type)

    # create vertical rivers without crossings
    rivers_v = sorted([pos for direction, pos in rivers if direction is v])
    for x in rivers_v:
        draw_line_vertical(state.grid, range(1, height - 1), x, object_type)

    # sample path to goal
    path = [h] * len(rivers_v) + [v] * len(rivers_h)
    rng.shuffle(path)

    # create crossing
    limits_h = [0] + rivers_h + [height - 1]  # horizontal river boundaries
    limits_v = [0] + rivers_v + [width - 1]  # vertical river boundaries
    room_i, room_j = 0, 0  # coordinates of current "room"
    for step_direction in path:
        if step_direction is h:
            i = rng.integers(limits_h[room_i] + 1, limits_h[room_i + 1])
            j = limits_v[room_j + 1]
            room_j += 1

        elif step_direction is v:
            i = limits_h[room_i + 1]
            j = rng.integers(limits_v[room_j] + 1, limits_v[room_j + 1])
            room_i += 1

        else:
            assert False

        state.grid[i, j] = Floor()

    # Place agent on top left
    state.agent.position = (1, 1)  # type: ignore
    state.agent.orientation = Orientation.E

    return state