def test_position_from_position_or_tuple(y: int, x: int): position = Position(y, x) position_from_position = Position.from_position_or_tuple(position) assert isinstance(position_from_position, Position) assert position_from_position is position position_from_tuple = Position.from_position_or_tuple((y, x)) assert isinstance(position_from_tuple, Position) assert position_from_position == position_from_tuple
def updated_agent_position_if_unobstructed(agent_pos: PositionOrTuple, agent_orientation: Orientation, action: Action) -> Position: """Returns the desired/intended position according to `action` NOTE: Assumes action is successful and next position is not blocking agent Args: agent_pos (`Position`): current agent position action (`Action`): action taken by agent Returns: Position: next position """ agent_pos = Position.from_position_or_tuple(agent_pos) if action not in TRANSLATION_ACTIONS: return agent_pos # Map directions to relative orientation direction_to_relative_orientation = { Action.MOVE_FORWARD: agent_orientation, Action.MOVE_LEFT: agent_orientation.rotate_left(), Action.MOVE_RIGHT: agent_orientation.rotate_right(), Action.MOVE_BACKWARD: agent_orientation.rotate_back(), } delta = direction_to_relative_orientation[action].as_position() return agent_pos + delta
def __init__( self, grid_shape: Shape, object_types: Sequence[Type[GridObject]], colors: Sequence[Color], ): if grid_shape.width % 2 == 0: raise ValueError('shape should have an odd width') self.grid_shape = grid_shape self.object_types = list(object_types) self.colors = set(colors) | {Color.NONE} self._grid_object_types = set(object_types) | {Hidden} self._agent_object_types = set(object_types) | {NoneGridObject} # TODO eventually let this substitute the `grid_shape` input altogether # this area represents the observable area, with (0, 0) representing # the agent's position, when the agent is pointing N self.area = Area( (-self.grid_shape.height + 1, 0), (-(self.grid_shape.width // 2), self.grid_shape.width // 2), ) # NOTE this position is relative to the top right coordinate of the area self.agent_position = Position(self.area.height - 1, self.area.width // 2)
def draw_cartesian_product(grid: Grid, ys: Iterable[int], xs: Iterable[int], factory: GridObjectFactory) -> List[Position]: """use factory-created grid-objects to draw on grid""" positions = [Position(y, x) for y in ys for x in xs] for position in positions: grid[position] = factory() return positions
def test_grid_set_item(): grid = Grid(3, 4) pos = Position(0, 0) obj = Floor() assert grid[pos] is not obj grid[pos] = obj assert grid[pos] is obj
def compute_ray( position: PositionOrTuple, area: Area, *, radians: float, step_size: float, unique: bool = True, ) -> Ray: """Returns a ray from a given position. A ray is a list of positions which are hit by a direct line starting at the center of the given position and moving along the given direction (in radians) until the area is left. Args: position (PositionOrTuple): initial position, must be in area. area (Area): boundary over rays. radians (float): ray direction. step_size (float): ray step granularity. unique (bool): If true, the same position can appear twice in the ray. Returns: Ray: ray from the given position until the area boundary """ if not area.contains(position): raise ValueError(f'position {position} must be inside area {area}') position = Position.from_position_or_tuple(position) y0, x0 = float(position.y), float(position.x) dy = step_size * math.sin(radians) dx = step_size * math.cos(radians) ys = (y0 + i * dy for i in itt.count()) xs = (x0 + i * dx for i in itt.count()) positions: Iterable[Position] positions = (Position(round(y), round(x)) for y, x in zip(ys, xs)) positions = itt.takewhile(area.contains, positions) positions = mitt.unique_everseen(positions) if unique else positions return list(positions)
def test_grid_swap(): grid = Grid(3, 4) # caching positions and objects before swap objects_before = { position: grid[position] for position in grid.positions() } pos1 = Position(0, 0) pos2 = Position(1, 1) grid.swap(pos1, pos2) # caching positions and objects after swap objects_after = {position: grid[position] for position in grid.positions()} # testing swapped objects assert objects_before[pos1] is objects_after[pos2] assert objects_before[pos2] is objects_after[pos1] # testing all other objects are the same for position in grid.positions(): if position not in (pos1, pos2): assert objects_before[position] is objects_after[position]
def test_proportional_to_distance_default( position: PositionOrTuple, kwargs, expected: float, forbidden_state_maker, forbidden_action_maker, ): state = forbidden_state_maker() action = forbidden_action_maker() next_state = make_5x5_goal_state() # TODO find better way to construct this state next_state.agent.position = Position.from_position_or_tuple(position) reward = proportional_to_distance(state, action, next_state, object_type=Goal, **kwargs) assert round(reward, 7) == expected
def frontal_line_of_sight( state: State, *, observation_space: ObservationSpace, rng: Optional[rnd.Generator] = None, # pylint: disable=unused-argument ) -> Observation: """only tiles in front of the agent can be seen""" # get agent's POV grid area = state.agent.get_pov_area(observation_space.area) observation_grid = state.grid.subgrid(area).change_orientation( state.agent.orientation) # hiding all tiles which are not directly in front of agent for position in observation_grid.positions(): if ( # the tile is not in the same column as the agent position.x == observation_space.agent_position.x # or the tile is behind the agent or position.y < observation_space.agent_position.y): observation_grid[position] = Hidden() # boolean flag to detect whether the line of sight has run into a # non-transparent object found_non_transparent = False # for every y-coordinate in front of the agent for y in range(observation_space.agent_position.y - 1, -1, -1): position = Position(y, observation_space.agent_position.x) # hide object if we have already encountered a non-transparent object if found_non_transparent: observation_grid[position] = Hidden() # update the boolean flag if object is non-transparent found_non_transparent |= not observation_grid[position].transparent observation_agent = Agent(observation_space.agent_position, Orientation.N, state.agent.obj) return Observation(observation_grid, observation_agent)
def compute_rays_fancy(position: PositionOrTuple, area: Area) -> List[Ray]: """Returns rays obtained by targeting edge points. A ray is a list of positions which are hit by a direct line starting at the center of the given position and moving along a direction until the area is left. This method will search in the directions towards all other cell edges. Args: position (PositionOrTuple): initial position, must be in area. area (Area): boundary over rays. Returns: List[Ray]: """ position = Position.from_position_or_tuple(position) # compute corners of each cell ys = np.linspace(area.ymin, area.ymax + 1, num=area.height + 1) - 0.5 xs = np.linspace(area.xmin, area.xmax + 1, num=area.width + 1) - 0.5 # center all positions ys = ys - position.y xs = xs - position.x # compute points and angles yys, xxs = np.meshgrid(ys, xs) radians = np.arctan2(yys, xxs) radians = np.sort(radians, axis=None) rays = [ compute_ray(position, area, radians=rad, step_size=0.01) for rad in radians ] return rays
def test_position_manhattan_distance(pos1: Position, pos2: Position, expected: float): assert Position.manhattan_distance(pos1, pos2) == expected
def test_position_euclidean_distance(pos1: Position, pos2: Position, expected: float): assert Position.euclidean_distance(pos1, pos2) == expected
assert visibility.all() @pytest.mark.parametrize( 'objects,position,expected_int', [ ( [ [Floor(), Floor(), Floor(), Floor(), Floor()], [Floor(), Floor(), Floor(), Floor(), Floor()], [Floor(), Floor(), Floor(), Floor(), Floor()], ], Position(2, 2), [ [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], ], ), ( [ [Floor(), Floor(), Floor(), Floor(), Floor()], [Floor(), Wall(), Wall(), Wall(), Floor()], [Floor(), Wall(), Floor(), Wall(), Floor()], ],
[ (Area((0, 1), (0, 2)), Area((0, 1), (0, 2)), True), (Area((0, 1), (0, 2)), Area((-1, 1), (-2, 2)), False), # (Area((-1, 1), (-2, 2)), Area((0, 1), (0, 2)), False), (Area((-1, 1), (-2, 2)), Area((-1, 1), (-2, 2)), True), ], ) def test_area_eq(area1: Area, area2: Area, expected: bool): assert (area1 == area2) == expected @pytest.mark.parametrize( 'orientation,dist,delta_position', [ (Orientation.N, 1, Position(-1, 0)), (Orientation.N, 2, Position(-2, 0)), # (Orientation.S, 1, Position(1, 0)), (Orientation.S, 2, Position(2, 0)), # (Orientation.E, 1, Position(0, 1)), (Orientation.E, 2, Position(0, 2)), # (Orientation.W, 1, Position(0, -1)), (Orientation.W, 2, Position(0, -2)), ], ) def test_orientation_as_position(orientation: Orientation, dist: int, delta_position: Position): assert orientation.as_position(dist) == delta_position
def test_grid_get_item(): grid = Grid(3, 4) pos = Position(0, 0) assert isinstance(grid[pos], Floor) assert grid[pos] is grid[pos]
((1, 2), Orientation.S, Area((1, 7), (-1, 5))), ((1, 2), Orientation.E, Area((-2, 4), (2, 8))), ((1, 2), Orientation.W, Area((-2, 4), (-4, 2))), ], ) def test_get_pov_area(position: PositionOrTuple, orientation: Orientation, expected: Area): relative_area = Area((-6, 0), (-3, 3)) agent = Agent(position, orientation) assert agent.get_pov_area(relative_area) == expected @pytest.mark.parametrize( 'position,orientation,delta_position,expected', [ ((0, 0), Orientation.N, Position(1, -1), (1, -1)), ((0, 0), Orientation.S, Position(1, -1), (-1, 1)), ((0, 0), Orientation.E, Position(1, -1), (-1, -1)), ((0, 0), Orientation.W, Position(1, -1), (1, 1)), ((1, 2), Orientation.N, Position(2, -2), (3, 0)), ((1, 2), Orientation.S, Position(2, -2), (-1, 4)), ((1, 2), Orientation.E, Position(2, -2), (-1, 0)), ((1, 2), Orientation.W, Position(2, -2), (3, 4)), ], ) def test_agent_position_relative( position: PositionOrTuple, orientation: Orientation, delta_position: Position, expected: PositionOrTuple, ):
def test_delta_position_rotate_basis( delta_position: Position, orientation: Orientation, expected: Position, ): assert delta_position.rotate(orientation) == expected